﻿using System.Collections.Generic;
using Engine;
using Engine.Graphics;
using Game;
using Mekiasm.Common;
using Mekiasm.Dust;

namespace Mekiasm
{
    public class FluidSystem
    {

        public static List<Element<Interface.IMekFluidMachine>> Elements = new List<Element<Interface.IMekFluidMachine>>();
        
        public static Dictionary<string, LinkedItem> RenderCache = new Dictionary<string, LinkedItem>();

        public class LinkedItem
        {
            public Texture2D Source;
            public RenderTarget2D Render;
            public int nextFrame;
            public int Frames;
            public float CoordEach;
            public bool Use;
            public bool FlipDirection;
        }

        public static void InitRenders()
        {
            var list = ContentManager.List("Mekiasm/Gui/Fluid");
            foreach (var item in list)
            {
                LinkedItem linkedItem = new LinkedItem();
                linkedItem.Source = ContentManager.Get<Texture2D>(item.ContentPath);
                linkedItem.Render = new RenderTarget2D(linkedItem.Source.Width, linkedItem.Source.Width, 1, ColorFormat.Rgba8888, DepthFormat.None);
                linkedItem.Frames = linkedItem.Source.Height / linkedItem.Source.Width;//计算帧数
                linkedItem.CoordEach = 1f / linkedItem.Frames;
                RenderCache.Add(item.Filename.Split(new char[] { '.' })[0], linkedItem);
            }
            UpdateRender(true);
        }

        private static PrimitivesRenderer2D primitivesRenderer2D = new PrimitivesRenderer2D();

        public static void UpdateRender(bool flag = false)
        {
            if (flag || Time.PeriodicEvent(0.15, 0.0))
            {
                foreach (var item in RenderCache.Values)
                {
                    RenderTarget2D tmp = Display.RenderTarget;                    
                    Display.RenderTarget = item.Render;
                    Display.Clear(Color.Transparent);
                    TexturedBatch2D texturedBatch = primitivesRenderer2D.TexturedBatch(item.Source,true,0,DepthStencilState.Default);
                    Vector2 start, end;
                    start = new Vector2(0, item.nextFrame * item.CoordEach);
                    end = new Vector2(1f, (item.nextFrame + 1) * item.CoordEach);
                    if (++item.nextFrame == item.Frames)
                    {
                        item.nextFrame = 0;
                    }
                    texturedBatch.QueueQuad(Vector2.Zero, new Vector2(item.Source.Width), 1f, start, end, Color.White);

                    primitivesRenderer2D.Flush();
                    Display.RenderTarget = tmp;
                }
            }
        }

        public static Matrix GetFluidRenderMatrix(ref Color renderColor, int value)
        {
            int contents = Terrain.ExtractContents(value);
            var block = BlocksManager.Blocks[contents];
            if (block is MekBlock mek)
            {
                var item = mek.GetItem(ref value);
                if (item is Interface.IAnimateBlockTexture m)
                {
                    renderColor = m.AnimateColorTransForm;
                    return m.AnimateTextureTranform;
                }
                else if (item is Interface.IInfuserableItem fm)
                {
                    if (fm.InfuserValue == CoalDust.Index)
                    {

                        renderColor = Color.Black;
                    }
                    else if (fm.InfuserValue == RedStoneDust.Index)
                    {

                        renderColor = Color.Red;
                    }
                    else if (fm.InfuserValue == DiamondDust.Index)
                    {
                        renderColor = Color.SkyBlue;
                    }
                    else if (fm.InfuserValue == SkyStoneDust.Index)
                    {

                        renderColor = Color.Purple;
                    }
                    else if (fm.InfuserValue == OsmiumDust.Index) {
                        renderColor = Color.DarkBlue;
                    }
                    return Matrix.Identity;
                }
            }
            switch (contents)
            {
                case MagmaBlock.Index:
                    return Matrix.CreateScale(0.0625f, 0.0625f, 1) * Matrix.CreateTranslation(0.8125f, 0.5f, 0);
                case WaterBlock.Index:
                    renderColor = BlockColorsMap.WaterColorsMap.Lookup(Terrain.ExtractTemperature(value), Terrain.ExtractHumidity(value));
                    return Matrix.CreateScale(0.0625f, 0.0625f, 1) * Matrix.CreateTranslation(0.8125f, 0.6875f, 0);
                default: return Matrix.Identity;
            }
        }

        public static Texture2D GetFluidRenderTexture(int value)
        {
            int contents = Terrain.ExtractContents(value);
            var block = BlocksManager.Blocks[contents];
            if (block is MekBlock mek)
            {
                var item = mek.GetItem(ref value);
                if (item is Interface.IAnimateBlockTexture m) return RenderCache[m.AnimateTextureName].Render;
                if (item is Interface.IInfuserableItem fm)
                {
                    return ContentManager.Get<Texture2D>("Mekiasm/Gui/InfuseType/base");
                }
            }
            return ILibrary.subsystemAnimatedTextures.AnimatedBlocksTexture;
        }

        public static void Add(Element<Interface.IMekFluidMachine> Element)
        {
            if (Elements.Find(p => p.Point == Element.Point) == null)
            {
                Elements.Add(Element);
                UpdateConnection(Element.Point);
            }
        }

        public static Element<Interface.IMekFluidMachine> Get(Point3 point3)
        {
            return Elements.Find(p => p.Point == point3);
        }

        public static Texture2D GetTexture(ComponentEnergyMachine.FuelType fuelType)
        {
            switch (fuelType)
            {
                case ComponentEnergyMachine.FuelType.Fliud:
                    return ILibrary.subsystemAnimatedTextures == null ? BlocksTexturesManager.DefaultBlocksTexture : ILibrary.subsystemAnimatedTextures.AnimatedBlocksTexture;
                default: return ILibrary.subsystemAnimatedTextures == null ? BlocksTexturesManager.DefaultBlocksTexture : ILibrary.subsystemAnimatedTextures.AnimatedBlocksTexture;
            }
        }
        public static void UpdateConnection(Point3 pointStart)
        {
            ClearFlags();
            updateConnectionsLogic(pointStart);
        }

        public static void updateConnectionsLogic(Point3 pointStart)
        {
            Element<Interface.IMekFluidMachine> StartElement = Elements.Find(p => p.Point == pointStart);
            if (StartElement == null || StartElement.Visited) return;
            StartElement.Visited = true;
            StartElement.Connections.Clear();
            for (int i = 0; i < 6; i++)
            {
                Point3 point3 = pointStart + CellFace.FaceToPoint3(i);
                Element<Interface.IMekFluidMachine> neibor = Elements.Find(p => p.Point == point3);
                if (neibor != null)
                {
                    StartElement.Connections.Add(neibor);
                }
                updateConnectionsLogic(point3);
            }
        }
        public static void ClearFlags()
        {
            for (int i = 0; i < Elements.Count; i++)
            {
                Elements[i].Visited = false;
            }
        }

        public static void Remove(Point3 point3)
        {
            var element = Elements.Find(p => p.Point == point3);
            if (element != null)
            {
                Elements.Remove(element);
                for (int i = 0; i < Elements.Count; i++)
                {
                    var element1 = Elements[i].Connections.Find(p => p.Point == point3);
                    if (element1 != null) Elements[i].Connections.Remove(element1);
                }
            }
            UpdateConnection(point3);
        }

        static float progress = 0;

        public static void Draw(Camera camera)
        {
            PrimitivesRenderer3D primitivesRenderer = new PrimitivesRenderer3D();
            BlockMesh blockMesh = new BlockMesh();
            foreach (var e in Elements)
            {
                if (e is FluidElement.FluidTankElement fe)
                {
                    var slot = e.Instance.Machine.Slots[e.Instance.Machine.AnimateRenderSlot];
                    progress = slot.FuelAmount / (float)slot.FuelCapacity;
                    if (progress > 0 && slot.FuelValue != 0)
                    {
                        Matrix scale = Matrix.CreateScale(1, progress, 1);
                        blockMesh.Indices.Clear();
                        blockMesh.Vertices.Clear();
                        blockMesh.AppendBlockMesh(FluidTank.InnerBox.BlockMesh);
                        blockMesh.GenerateSidesData();
                        blockMesh.TransformTextureCoordinates(GetFluidRenderMatrix(ref fe.RenderColor, slot.FuelValue));
                        TexturedBatch3D textured = primitivesRenderer.TexturedBatch(GetFluidRenderTexture(slot.FuelValue), true);
                        int start = textured.TriangleVertices.Count;
                        var indies = blockMesh.Indices;
                        var vertices = blockMesh.Vertices;
                        for (int i = 0; i < indies.Count; i += 3)
                        {
                            var ver1 = vertices[indies[i]];
                            var ver2 = vertices[indies[i + 1]];
                            var ver3 = vertices[indies[i + 2]];
                            textured.QueueTriangle(ver1.Position, ver2.Position, ver3.Position, ver1.TextureCoordinates, ver2.TextureCoordinates, ver3.TextureCoordinates, fe.RenderColor);
                        }
                        textured.TransformTriangles(scale * Matrix.CreateTranslation(e.Position), start);
                    }
                }
            }
            primitivesRenderer.Flush(camera.ViewProjectionMatrix);
        }

        public static void Update(float dt)
        {
            for (int i = 0; i < Elements.Count; i++)
            {
                Elements[i].Simulate();
            }
        }
        public static void Dispose()
        {
            Elements.Clear();
        }
    }
}
